package org.aliyunoss.aop;

import cn.hutool.core.io.FileTypeUtil;
import org.aliyunoss.utils.MyFileUtils;
import org.aliyunoss.vo.ErrorCode;
import org.aliyunoss.vo.MyAppException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.processing.FilerException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Aspect
@Component
public class FileLimitAop {
    // 定义默认的单个文件最大限制 5MB 。5Mb = 5 * 1024 * 1024 byte
    private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;
    private static final long MAX_REQUEST_SIZE = 50 * 1024 * 1024;

    // 注意，这里要指定注解的全限定类名。不然无法进入AOP拦截自定义注解FileLimit
    @Pointcut("@annotation(org.aliyunoss.aop.FileLimit)")
    public void pointcut() {
    }

    /**
     * 方法体执行之前执行
     */
    @Before("pointcut()")
    public void beforeLog(JoinPoint joinPoint) throws FilerException {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        FileLimit annotation = AnnotationUtils.getAnnotation(signature.getMethod(), FileLimit.class);
        if (null == annotation) {
            return;
        }
        // 执行文件检查
        fileSizeLimit(joinPoint, annotation);
    }

    // 判定文件大小是否合格，如果不合格，直接跑出自定义异常FileLimitException。进而阻塞方法正常进行。
    private void fileSizeLimit(JoinPoint joinPoint, FileLimit annotation) throws FilerException {
        // 获取AOP签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取注解的指定最大文件大小
        Map annotationMaxFileSize = getAnnotationMaxFileSize(annotation);
        long maxFileSize = (long) annotationMaxFileSize.get("maxFileSize");
        long maxRequestSize = (long) annotationMaxFileSize.get("maxRequestSize");
        // 通过AOP签名 获取接口参数，调用方法获取文件
        //=
        List<MultipartFile> multipartFileList= new ArrayList<>();
        Object[] args = joinPoint.getArgs();

        for (Object arg : args) {
            multipartFileList = (List<MultipartFile>) arg;
        }
        //保存总文件大小
        long maxSize = 0;
        for (MultipartFile multipartFile : multipartFileList) {
            if (null != multipartFile) {
                long size = multipartFile.getSize();
                if (0 == size) {
                    //自定义异常
                    throw new MyAppException(ErrorCode.FILE_DATA_EXCEPTION.getCode(), "文件数据（大小为0）异常");
                }
                maxSize += size;
                if (multipartFile.getSize() > maxFileSize) {
                    String msg = "文件大小不得超过 " + annotation.max() + annotation.unit().toString();
                    //System.out.println(msg);
                    throw new MyAppException(ErrorCode.FILE_DATA_EXCEPTION.getCode(), msg);
                }
            } else {
                throw new MyAppException(ErrorCode.FILE_DATA_EXCEPTION.getCode(), "文件为null");
            }
        }

        if (maxSize > maxRequestSize) {
            throw new MyAppException(ErrorCode.FILE_DATA_EXCEPTION.getCode(), "单次上传总文件大小不能超过" + annotation.maxRequestSize() + annotation.unit());
        }

        List fileFormatLimit = getFileFormatLimit(annotation);
        //判断文件格式 根据文件头信息判断
        for (MultipartFile multipartFile : multipartFileList) {
            String type = FileTypeUtil.getType(MyFileUtils.multipartFileToFile(multipartFile));
            //判断类型是String类型，因此可以直接用contains方法
            boolean contains = fileFormatLimit.contains(type);
            if (!contains){
                throw  new MyAppException(ErrorCode.FILE_DATA_EXCEPTION.getCode(), "文件格式不正确");
            }
        }
    }

    // 获取使用注解指定最大文件大小。如果没有指定文件大小，就用默认值
    public Map getAnnotationMaxFileSize(FileLimit fileLimit) {
        Map map = new HashMap();

        if (null == fileLimit) {
            map.put("maxFileSize", MAX_FILE_SIZE);
            map.put("maxRequestSize", MAX_REQUEST_SIZE);
            return map;
        }
        switch (fileLimit.unit()) {
            case MB:
                map.put("maxFileSize", (long) fileLimit.max() << 20);
                map.put("maxRequestSize", (long) fileLimit.maxRequestSize() << 20);
                return map;
            case KB:
                map.put("maxFileSize", (long) fileLimit.max() << 10);
                map.put("maxRequestSize", (long) fileLimit.maxRequestSize() * 1024);
                return map;
            default:
                map.put("maxFileSize", MAX_FILE_SIZE);
                map.put("maxRequestSize", MAX_REQUEST_SIZE);
                return map;
        }
    }

    //    获取上传文件类型限制
    public List getFileFormatLimit(FileLimit fileLimit) {
        List fileFormatLimitList = new ArrayList();
        if (null == fileLimit) {
            fileFormatLimitList.add("bmp");
            fileFormatLimitList.add("gif");
            fileFormatLimitList.add("ico");
            fileFormatLimitList.add("jfif");
            fileFormatLimitList.add("jpeg");
            fileFormatLimitList.add("jpg");
            fileFormatLimitList.add("png");
            fileFormatLimitList.add("tif");
            fileFormatLimitList.add("tiff");
            fileFormatLimitList.add("webp");
            fileFormatLimitList.add("wbmp");
            ////bmp/gif/jpg/jfif/jpeg/png/webp/wbmp/ico/tif/tiff
            //JSONObject jso1 = new JSONObject();
            //jso1.put("type", "BMP");
            //jso1.put("contentType", "image/bmp");
            //JSONObject jso2 = new JSONObject();
            //jso2.put("type", "gif");
            //jso2.put("contentType", "image/gif");
            //JSONObject jso3 = new JSONObject();
            //jso3.put("type", "jfif");
            //jso3.put("contentType", "image/jpeg");
            //JSONObject jso4 = new JSONObject();
            //jso4.put("type", "jpeg");
            //jso4.put("contentType", "image/jpg");
            //JSONObject jso5 = new JSONObject();
            //jso5.put("type", "jpg");
            //jso5.put("contentType", "image/jpg");
            //JSONObject jso6 = new JSONObject();
            //jso6.put("type", "webp");
            //jso6.put("contentType", "image/webp");
            //JSONObject jso7 = new JSONObject();
            //jso7.put("type", "png");
            //jso7.put("contentType", "image/png");
            //JSONObject jso8 = new JSONObject();
            //jso8.put("type", "tif");
            //jso8.put("contentType", "image/tiff");
            //JSONObject jso9 = new JSONObject();
            //jso9.put("type", "tiff");
            //jso9.put("contentType", "image/tiff");
            //JSONObject jso10 = new JSONObject();
            //jso10.put("type", "ico");
            //jso10.put("contentType", "image/x-icon");
            //JSONObject jso11 = new JSONObject();
            //jso11.put("type", "wbmp");
            //jso11.put("contentType", "image/vnd.wap.wbmp");
            //
            //fileFormatLimitList.add(jso1);
            //fileFormatLimitList.add(jso2);
            //fileFormatLimitList.add(jso3);
            //fileFormatLimitList.add(jso4);
            //fileFormatLimitList.add(jso5);
            //fileFormatLimitList.add(jso6);
            //fileFormatLimitList.add(jso7);
            //fileFormatLimitList.add(jso8);
            //fileFormatLimitList.add(jso9);
            //fileFormatLimitList.add(jso10);
            //fileFormatLimitList.add(jso11);
            return fileFormatLimitList;
        }
        switch (fileLimit.fileFormat()) {
            case "images":
                fileFormatLimitList.add("bmp");
                fileFormatLimitList.add("gif");
                fileFormatLimitList.add("ico");
                fileFormatLimitList.add("jfif");
                fileFormatLimitList.add("jpeg");
                fileFormatLimitList.add("jpg");
                fileFormatLimitList.add("png");
                fileFormatLimitList.add("tif");
                fileFormatLimitList.add("tiff");
                fileFormatLimitList.add("webp");
                fileFormatLimitList.add("wbmp");
                return fileFormatLimitList;
            case "videos":
                fileFormatLimitList.add("avi");
                fileFormatLimitList.add("flv");
                fileFormatLimitList.add("mp4");
                fileFormatLimitList.add("mpeg");//
                fileFormatLimitList.add("wmv");
                fileFormatLimitList.add("wma");//
                fileFormatLimitList.add("w4a");//
                fileFormatLimitList.add("wov");//
                fileFormatLimitList.add("3GP");//
                fileFormatLimitList.add("webm");//
                fileFormatLimitList.add("vob");//
                fileFormatLimitList.add("mkv");//
                return fileFormatLimitList;
        }
        return fileFormatLimitList;
    }
            //case "AVI": contentType = "video/avi";break;
            //case "FLV": contentType = "video/x-flv";break;
            //case "MP4": contentType = "video/mpeg4";break;
            //case "MPEG": contentType = "video/mpg";break;
            //case "WMV": contentType = "video/x-ms-wmv";break;
            //case "WMA": contentType = "video/wma";break;
            //case "W4A": contentType = "video/mp4";break;
            //case "W4V": contentType = "video/mp4";break;
            //case "WOV": contentType = "video/quicktime";break;
            //case "3GP": contentType = "video/3gpp";break;
            //case "WEBM": contentType = "video/webm";break;
            //case "VOB": contentType = "video/vob";break;
            //case "MKV": contentType = "video/x-matroska";break;
}